﻿/*
 * The functionality of this program (for the RefreshLinkedReportPageProperties action)
 * can be accomplished by running the following query on the report server database
 * UPDATE L SET Property = p.Property
 * FROM ReportServer.DBO.CATALOG L
 * JOIN
 * ReportServer.DBO.CATALOG P ON P.ItemID = L.LinkSourceID
 * where L.TYPE = 4 AND
 * CONVERT( VARCHAR(MAX), L.Property ) <> CONVERT( VARCHAR(MAX), P.Property)

 * See http://social.msdn.microsoft.com/Forums/en-US/sqlreportingservices/thread/c55195b7-c7a8-48e7-8c79-d1d7e0758a5f
 * for more information
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// get reference to our Web Reference we set up earlier
using rs = rscons.ReportingService2010;

namespace rsconsole
{
    class Program
    {
        static void Main(string[] args)
        {
#region CommandLineArguments
            rsconsoleArguments arguments = new rsconsoleArguments(args);
            Boolean ArgUrl = false;
            Boolean ArgPath = false;
            Boolean ArgAction = false;

            // check for valid URL
            if (Uri.IsWellFormedUriString(arguments.Url, UriKind.RelativeOrAbsolute))
            {
                // url is valid
                //check for valid ReportService2010.asmx at the end of the url
                if (arguments.Url.LastIndexOf("ReportService2010.asmx") > 0)
                {
                    // everything looks good, print off the url
                    Console.WriteLine("Url: " + arguments.Url);
                    ArgUrl = true;
                }
                else
                {
                    // URL is not a report services URL
                    Console.WriteLine("Invalid report services URL entered.  URL Must be in the following format:");
                    Console.WriteLine("http://wpdotappl06/ReportServer/ReportService2010.asmx");
                }
            } else {
                // URL is not valid
                Console.WriteLine("Invalid URL entered.  URL Must be in the following format:");
                Console.WriteLine("http://wpdotappl06/ReportServer/ReportService2010.asmx");
            }
            
            if (arguments.Path.Length > 0)
            {
                // need to implment checking of the path someday.  For now just print off the path
                Console.WriteLine("Path: " + arguments.Path);
                ArgPath = true;
            }
            // this should be defaulted to true
            if (arguments.RefreshLinkedReportPageProperties)
            {
                Console.WriteLine("RefreshLinkedReportPageProperties: " + arguments.RefreshLinkedReportPageProperties);
                ArgAction = true;
            }
#endregion CommandLineArguments

#region Main
            //check to insure all the arguments have valid values
            if (ArgUrl && ArgPath && ArgAction)
            {
                // most of code below came from
                // http://bengribaudo.com/blog/2012/01/30/1851/ssrs-updating-linked-report-page-properties
                // get reference to the report service
                var service = new rs.ReportingService2010();

                // set the url to be whatever was passed from the arguments
                service.Url = arguments.Url;

                // get a catalogitem[] object
                rs.CatalogItem[] reports = null;
                // get all catalog item objects under the path
                reports = service.ListChildren(arguments.Path, true);

                // List of properties to copy from parent to linked reports.
                var requestedProperties = new rs.Property[] {
                    new rs.Property { Name = "PageHeight" },
                    new rs.Property { Name = "PageWidth" },
                    new rs.Property { Name = "TopMargin" },
                    new rs.Property { Name = "BottomMargin" },
                    new rs.Property { Name = "LeftMargin" },
                    new rs.Property { Name = "RightMargin" }
                };

                foreach (var report in reports)
                {
                    Console.Write("{0} ({1})", report.Name, report.Path);

                    if (report.TypeName == "LinkedReport")
                    {
                        string parentReportLink = null;
                        parentReportLink = service.GetItemLink(report.Path);

                        // Fetch interested property values from parent report.
                        rs.Property[] parentValues = null;
                        parentValues = service.GetProperties(parentReportLink, requestedProperties);

                        // Set the child report's properties to the just-fetched values.
                        //service.SetProperties(report.Path, parentValues);
                        Console.WriteLine(" -- properties updated from {0}", parentReportLink);
                    }
                    else
                    {
                        Console.WriteLine(" -- non-linked - skipped");
                    }
                }
            }
#endregion Main
        }

    }

    /// <summary>
    /// Derived class to handle the specific command line arguments
    /// we are expecting for rsconsole
    /// </summary>
    public class rsconsoleArguments : InputArguments
    {
        public string Url
        {
            get { return GetValue("url"); }
        }

        public string Path
        {
            get { return GetValue("path"); }
        }

        public bool RefreshLinkedReportPageProperties
        {
            get { return GetBoolValue("-RefreshLinkedReportPageProperties"); }
        }

        public rsconsoleArguments(string[] args)
            : base(args)
        {
        }

        protected bool GetBoolValue(string key)
        {
            string adjustedKey;
            if (ContainsKey(key, out adjustedKey))
            {
                bool res;
                bool.TryParse(_parsedArguments[adjustedKey], out res);
                return res;
            }
            return false;
        }
    }

    /// <summary>
    /// Handles parsing of command line arguments
    /// from http://dotnetfollower.com/wordpress/2012/03/c-simple-command-line-arguments-parser/
    /// </summary>
    public class InputArguments
    {
        #region fields & properties
        public const string DEFAULT_KEY_LEADING_PATTERN = "-";

        protected Dictionary<string, string> _parsedArguments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        protected readonly string _keyLeadingPattern;

        public string this[string key]
        {
            get { return GetValue(key); }
            set
            {
                if (key != null)
                    _parsedArguments[key] = value;
            }
        }
        public string KeyLeadingPattern
        {
            get { return _keyLeadingPattern; }
        }
        #endregion

        #region public methods
        public InputArguments(string[] args, string keyLeadingPattern)
        {
            _keyLeadingPattern = !string.IsNullOrEmpty(keyLeadingPattern) ? keyLeadingPattern : DEFAULT_KEY_LEADING_PATTERN;

            if (args != null && args.Length > 0)
                Parse(args);
        }
        public InputArguments(string[] args)
            : this(args, null)
        {
        }

        public bool Contains(string key)
        {
            string adjustedKey;
            return ContainsKey(key, out adjustedKey);
        }

        public virtual string GetPeeledKey(string key)
        {
            return IsKey(key) ? key.Substring(_keyLeadingPattern.Length) : key;
        }
        public virtual string GetDecoratedKey(string key)
        {
            return !IsKey(key) ? (_keyLeadingPattern + key) : key;
        }
        public virtual bool IsKey(string str)
        {
            return str.StartsWith(_keyLeadingPattern);
        }
        #endregion

        #region internal methods
        protected virtual void Parse(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i] == null) continue;

                string key = null;
                string val = null;

                if (IsKey(args[i]))
                {
                    key = args[i];

                    if (i + 1 < args.Length && !IsKey(args[i + 1]))
                    {
                        val = args[i + 1];
                        i++;
                    }
                }
                else
                    val = args[i];

                // adjustment
                if (key == null)
                {
                    key = val;
                    val = null;
                }
                _parsedArguments[key] = val;
            }
        }

        protected virtual string GetValue(string key)
        {
            string adjustedKey;
            if (ContainsKey(key, out adjustedKey))
                return _parsedArguments[adjustedKey];

            return null;
        }

        protected virtual bool ContainsKey(string key, out string adjustedKey)
        {
            adjustedKey = key;

            if (_parsedArguments.ContainsKey(key))
                return true;

            if (IsKey(key))
            {
                string peeledKey = GetPeeledKey(key);
                if (_parsedArguments.ContainsKey(peeledKey))
                {
                    adjustedKey = peeledKey;
                    return true;
                }
                return false;
            }

            string decoratedKey = GetDecoratedKey(key);
            if (_parsedArguments.ContainsKey(decoratedKey))
            {
                adjustedKey = decoratedKey;
                return true;
            }
            return false;
        }
        #endregion
    }
}
